﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using FluxJpeg.Core;
using FluxJpeg.Core.Decoder;
using FluxJpeg.Core.Encoder;
using FluxJpeg.Core.Filtering;
using Microsoft.Windows.Data.DomainServices;
using MVVMtutorialAdvanced.Web;
using Riba.MVVMSL;

namespace MVVMtutorialAdvanced
{
	public class Books_VM : ViewModelBase
	{
		private ObservableCollection<CountryDTO> _countries;
		public ObservableCollection<CountryDTO> Countries
		{
			get { return _countries; }
			set { SetPropertyValue(ref _countries, value, () => Countries); }
		}

		private DomainCollectionView _entityList;  //use instead of PagedCollectionView
		public DomainCollectionView EntityList
		{
			get { return _entityList; }
			set { SetPropertyValue(ref _entityList, value, () => EntityList); }
		}

		private BookDTO _currentEntity;
		public BookDTO CurrentEntity
		{
			get { return _currentEntity; }
			set
			{
				var isNewEntity = false;
				if (_currentEntity != value)
				{
					SetIsDirty(false);
					isNewEntity = true;
				}
				SetPropertyValue(ref _currentEntity, value, () => CurrentEntity);


				//To assure, that CurrentBookAuthor is initially set to first BookAuthor
				CurrentBookAuthor = null;

				if (value == null)
				{
					DetailsVisibility = Visibility.Collapsed;
					SetIsDirty(false);
					return;
				}
				DetailsVisibility = Visibility.Visible;

				if (value.BookAuthorDtos.Count > 0)
					CurrentBookAuthor = value.BookAuthorDtos.ToArray()[0];

				if (isNewEntity)
					CurrentEntity.PropertyChanged += SelectedEntity_PropertyChanged; //CheckBox: Checked-Event, TextBox: TextChanged-Event (see BindingHelper)
			}
		}

		private Visibility _detailsVisibility;
		public Visibility DetailsVisibility
		{
			get { return _detailsVisibility; }
			set { SetPropertyValue(ref _detailsVisibility, value, () => DetailsVisibility); }
		}

		private BookAuthorDTO _currentBookAuthor;
		public BookAuthorDTO CurrentBookAuthor
		{
			get { return _currentBookAuthor; }
			set { SetPropertyValue(ref _currentBookAuthor, value, () => CurrentBookAuthor); }
		}

		public RelayCommand CommandInsertEntity { get; private set; }
		public RelayCommand CommandDeleteEntity { get; private set; }
		public RelayCommand CommandRefreshEntities { get; private set; }
		public RelayCommand CommandRevertEntity { get; private set; }
		public RelayCommand CommandSaveEntity { get; private set; }

		public RelayCommand CommandLoadImage { get; private set; }
		public RelayCommand CommandAddAuthor { get; private set; }
		public RelayCommand CommandDeleteAuthor { get; private set; }

		private readonly DomainServices.IDomainServiceBook _domainService;

		public Books_VM(DomainServices.IDomainServiceBook domainService)
		{
			_domainService = domainService;

			CommandInsertEntity = new RelayCommand(OnInsertEntity);
			CommandDeleteEntity = new RelayCommand(OnDeleteEntity);
			CommandRefreshEntities = new RelayCommand(OnLoadEntities);
			CommandRevertEntity = new RelayCommand(OnRevertEntity);
			CommandSaveEntity = new RelayCommand(OnSaveEntity);

			CommandAddAuthor = new RelayCommand(OnAddAuthor);
			CommandDeleteAuthor = new RelayCommand(OnDeleteAuthor);
			CommandLoadImage = new RelayCommand(OnLoadImage);

			GetItems();
		}

		private void OnLoadEntities(object commandData)
		{
			GetItems();
		}

		private void GetItems()
		{
			IsBusy = true;

			_domainService.GetCountries
				(
					(data, error) =>
						{
							if (error != null)
							{
								RaiseShowMsgBox(new ShowMsgBoxEventArgs(error.Message, "Städte lesen"));
								return;
							}

							Countries = new ObservableCollection<CountryDTO>(data);
						}
				);

			_domainService.GetBooks
				(
					(data, error) =>
						{
							IsBusy = false;

							if (error != null)
							{
								RaiseShowMsgBox(new ShowMsgBoxEventArgs(error.Message, "Bücher lesen"));
								return;
							}

							EntityList = data;

							EntityList.CurrentChanged += EntityList_CurrentChanged;
							EntityList_CurrentChanged(this, new EventArgs()); //Must be called also the first time after initialization ??????????????????????????????????????????????

							EntityList.MoveCurrentToFirst();
							
							CommandAddAuthor.IsEnabled = true;
							CommandDeleteAuthor.IsEnabled = true;
							CommandLoadImage.IsEnabled = true;
						}
				);
		}

		void EntityList_CurrentChanged(object sender, EventArgs e)
		{
			CurrentEntity = EntityList.CurrentItem as BookDTO; //Used in Design Mode to assure that CurrentEntity is initially filled
		}

		void SelectedEntity_PropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			SetIsDirty(true);
		}

		private void SetIsDirty(bool isDirty)
		{
			CommandInsertEntity.IsEnabled = !isDirty;
			CommandDeleteEntity.IsEnabled = !isDirty;
			CommandRefreshEntities.IsEnabled = !isDirty;
			CommandRevertEntity.IsEnabled = isDirty;
			CommandSaveEntity.IsEnabled = isDirty;

			if (isDirty)
			{
				if (!EntityList.IsAddingNew && !EntityList.IsEditingItem)
					EntityList.EditItem(EntityList.CurrentItem);
			}

			IsDirty = isDirty;
		}

		private void OnAddAuthor(object commandData)
		{
			var args = new LoadChildWindowEventArgs(typeof(BookAuthors_UC), EntityList.CurrentItem, CallbackFromChildWindowAddAuthor);
			RaiseLoadChildWindow(args);
		}

		private void CallbackFromChildWindowAddAuthor(ViewModelBase sender, object data)
		{
			var authorQdto = data as AuthorQDTO;
			if (authorQdto == null)
			  return;

			//Already added
			if (((BookDTO)EntityList.CurrentItem).BookAuthorDtos.Where(a => a.AuthorId == authorQdto.Id).Count() > 0)
				return;

			var bookAuthorDto = new BookAuthorDTO
				{
					BookId = ((BookDTO)EntityList.CurrentItem).Id,
					AuthorId = authorQdto.Id,
					AuthorName = string.Format("{0} {1}", authorQdto.FirstName, authorQdto.LastName)
				};
			((BookDTO)EntityList.CurrentItem).BookAuthorDtos.Add(bookAuthorDto);

			SetIsDirty(true);
		}

		private void OnDeleteAuthor(object commandData)
		{
			if (CurrentBookAuthor == null)
				return;

			((BookDTO)EntityList.CurrentItem).BookAuthorDtos.Remove(CurrentBookAuthor);

			SetIsDirty(true);
		}

		private void OnLoadImage(object commandData)
		{
			var openFileDialogEventArgs = new OpenFileDialogEventArgs("JPG Files (*.jpg)|*.jpg", 1, false, OpenFileDialogCallback);
			RaiseOpenFileDialog(openFileDialogEventArgs);
		}

		private void OpenFileDialogCallback(IEnumerable<FileInfo> files)
		{
			if (files.Count() == 0)
				return;

			var file = files.FirstOrDefault();
			var fileStream = file.OpenRead();

			using (fileStream)
			{
				const int maxSize = 700;

				var jpegIn = new JpegDecoder(fileStream).Decode(); //http://code.google.com/p/fjcore/

				if (!ImageResizer.ResizeNeeded(jpegIn.Image, maxSize))
				{
					var bytes = new byte[fileStream.Length];
					fileStream.Read(bytes, 0, (int)fileStream.Length);
					((BookDTO)EntityList.CurrentItem).Cover = bytes;
				}
				else
				{
					// Resize
					var resizedImage = new ImageResizer(jpegIn.Image).Resize(maxSize, ResamplingFilters.NearestNeighbor);
					var jpegOut = new DecodedJpeg(resizedImage, jpegIn.MetaHeaders); // Retain EXIF details

					// Encode
					var outStream = new MemoryStream();
					new JpegEncoder(jpegOut, 90, outStream).Encode();

					// To array 
					outStream.Seek(0, SeekOrigin.Begin);

					((BookDTO)EntityList.CurrentItem).Cover = outStream.ToArray();
				}
			}
		}

		private void OnInsertEntity(object commandData)
		{
			if (EntityList.IsEditingItem)
				EntityList.CancelEdit();

			SetIsDirty(true);
			EntityList.AddNew();
			((BookDTO)EntityList.CurrentItem).CountryId = 1; // Default = -
		}

		private void OnDeleteEntity(object commandData)
		{
			if (EntityList.CurrentItem == null)
				return;

			RaiseShowMsgBox(new ShowMsgBoxEventArgs("Wollen Sie das Buch wirklich löschen?", "Buch löschen", MessageBoxButton.OKCancel, DeleteBookCallback));
		}

		private void DeleteBookCallback(MessageBoxResult result)
		{
			if (result == MessageBoxResult.OK)
			{
				if (EntityList.IsEditingItem)
					EntityList.CancelEdit();

				IsBusy = true;
				_domainService.DeleteBook((BookDTO)EntityList.CurrentItem, OnBookDeleted);
			}
		}

		private void OnBookDeleted(Exception exception)
		{
			IsBusy = false;

			_domainService.AcceptChanges(); // ??????????????????????????????????????????????????????????
			EntityList.CommitEdit();
			SetIsDirty(false);

			if (EntityList.IsCurrentAfterLast)
			{
				try
				{
					EntityList.MoveToPreviousPage();
				}
				catch (Exception ex)
				{
					Debug.WriteLine(ex.Message);
				}
			}
		}

		private void OnRevertEntity(object commandData)
		{
			if (EntityList.CurrentItem == null)
				return;

			//Workaround ???????????????????????????????????????????????????????????????????
			((BookDTO)EntityList.CurrentItem).Price = 0; //RejectChanges does not work if non numeric Data is in the bound TextBox
			_domainService.RejectChanges();

			if (EntityList.IsEditingItem)
				EntityList.CancelEdit();
			else if (EntityList.IsAddingNew)
				EntityList.CancelNew();

			SetIsDirty(false);
		}

		private void OnSaveEntity(object commandData)
		{
			if (EntityList.CurrentItem == null)
				return;
			if (((BookDTO)EntityList.CurrentItem).HasValidationErrors)
			{
				RaiseShowMsgBox(new ShowMsgBoxEventArgs("Bitte zuerst Fehler korrigieren", "Speichern"));
				return;
			}
			if (EntityList.IsEditingItem && !((BookDTO)EntityList.CurrentItem).HasChanges)
			{
				RaiseShowMsgBox(new ShowMsgBoxEventArgs("Keine Änderungen", "Speichern"));
				return;			
			}
			
			if (EntityList.IsEditingItem)
			{
				EntityList.CommitEdit();
				IsBusy = true;
				_domainService.UpdateBook(((BookDTO)EntityList.CurrentItem), OnBookUpdated);
			}
			else if (EntityList.IsAddingNew)
			{
				EntityList.CommitNew();
				IsBusy = true;
				_domainService.InsertBook((BookDTO)EntityList.CurrentItem, OnBookUpdated);
			}
		}

		private void OnBookUpdated(Exception exception)
		{
			IsBusy = false;

			if (exception == null)
			{
				SetIsDirty(false);

				if (EntityList.IsEditingItem)
					EntityList.CommitEdit(); //Occurs after adding a new item -> order is no more possible, if CommitEdit is not run
				if (EntityList.IsAddingNew)
					EntityList.CommitNew();

				return;
			}

			RaiseShowMsgBox(new ShowMsgBoxEventArgs(exception.Message, "Fehler in OnBookUpdated"));
		}
	}
}